%%% -------------------------------------------------------------------
%%% Author  : Administrator
%%% Description :
%%%
%%% Created : 2009-3-20
%%% -------------------------------------------------------------------
-module(ring_lord).

-behaviour(gen_server).
%% --------------------------------------------------------------------
%% Include files
%% --------------------------------------------------------------------
-include("ring.hrl").

%% --------------------------------------------------------------------
%% External exports
-export([start_link/0]).
-export([add_ringnode/1, add_ringaccessor/1, get_ringnodes/0, add_benchnode/1, get_benchnodes/0]).

-export([list_ringnodes/0, list_ringaccessores/0, list_benchnodes/0]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

-record(state, {}).

%% ====================================================================
%% External functions
%% ====================================================================
start_link() ->
    log4erl:info("starting ring_lord server"),
    gen_server:start_link({global, ?MODULE}, ?MODULE, [], []).

list_ringnodes() ->
    ets:tab2list(ring_nodes).

list_ringaccessores() ->
    ets:tab2list(ring_accessores).

list_benchnodes() ->
    ets:tab2list(bench_nodes).

%% ====================================================================
%% Server functions
%% ====================================================================
-spec add_ringnode(RingNode :: ring_node()) -> ok | error.
add_ringnode(#ring_node{} = RingNode) ->
    gen_server:call({global, ?MODULE}, {add, RingNode}).

add_ringaccessor(#ring_accessor{} = RingAccessor) ->
    gen_server:call({global, ?MODULE}, {add, RingAccessor}).

-spec add_benchnode(BenchNode :: node()) -> ok | error.
add_benchnode(BenchNode) ->
    gen_server:call({global, ?MODULE}, {add, #bench_node{name=BenchNode}}).

-spec get_benchnodes() -> [node()].
get_benchnodes() ->
    gen_server:call({global, ?MODULE}, {get_all_benchnodes}).

update_ringaccessores() ->
    RingAccessores = ets:tab2list(ring_accessores),
    RingNodes = ets:tab2list(ring_nodes),
    update_ringaccessores(RingAccessores, RingNodes).

update_ringaccessores([], _RingNodes) ->
    ok;
update_ringaccessores([RingAccessor|Rest], RingNodes) ->
    gen_server:call({RingAccessor#ring_accessor.name, RingAccessor#ring_accessor.node}, {update_ringnodes, RingNodes}),
    update_ringaccessores(Rest, RingNodes).
    
-spec get_ringnodes() -> [ring_node()].
get_ringnodes() ->
    gen_server:call({global, ?MODULE}, {get_all_ringnodes}).

%% --------------------------------------------------------------------
%% Function: init/1
%% Description: Initiates the server
%% Returns: {ok, State}          |
%%          {ok, State, Timeout} |
%%          ignore               |
%%          {stop, Reason}
%% --------------------------------------------------------------------
init([]) ->
    log4erl:info("ring lord init"),
    
    MonitorOpt = [{node_type, visible}, nodedown_reason],
    net_kernel:monitor_nodes(true, MonitorOpt),

    ets:new(ring_nodes, [named_table, ordered_set, protected, {keypos, #ring_node.id}]),
    ets:new(ring_accessores, [named_table, set, protected, {keypos, #ring_accessor.id}]),
    ets:new(bench_nodes, [named_table, set, protected, {keypos, #bench_node.name}]),
    
    {ok, #state{}}.

%% --------------------------------------------------------------------
%% Function: handle_call/3
%% Description: Handling call messages
%% Returns: {reply, Reply, State}          |
%%          {reply, Reply, State, Timeout} |
%%          {noreply, State}               |
%%          {noreply, State, Timeout}      |
%%          {stop, Reason, Reply, State}   | (terminate/2 is called)
%%          {stop, Reason, State}            (terminate/2 is called)
%% --------------------------------------------------------------------
handle_call({add, #ring_accessor{}=RingAccessor}, _From, State) ->   % add a gen server
    log4erl:debug("add a new ring_accessor: ~p", [RingAccessor]),
    ets:insert(ring_accessores, RingAccessor),
    Reply = ets:tab2list(ring_nodes),
    {reply, Reply, State};
handle_call({add, #ring_node{}=RingNode}, _From, State) ->          % add a node
    log4erl:debug("add a new ring_node: ~p", [RingNode]),
    ets:insert(ring_nodes, RingNode),
    update_ringaccessores(),
    Reply = ok,
    {reply, Reply, State};
handle_call({add, #bench_node{name=Node}=BenchNode}, _From, State) ->
    log4erl:debug("add a benchmark node: ~p", [Node]),
    ets:insert(bench_nodes, BenchNode),
    {reply, ok, State};
handle_call({get_all_ringnodes}, _From, State) ->
    log4erl:debug("get all nodes in ring"),
    Reply = ets:tab2list(ring_nodes),
    {reply, Reply, State};
handle_call({get_all_benchnodes}, _From, State) ->
    BenchNodes = ets:tab2list(bench_nodes),
    {reply, BenchNodes, State};
handle_call(Request, _From, State) ->
    log4erl:debug("LORD: who call me: ~p", [Request]),
    Reply = ok,
    {reply, Reply, State}.

%% --------------------------------------------------------------------
%% Function: handle_cast/2
%% Description: Handling cast messages
%% Returns: {noreply, State}          |
%%          {noreply, State, Timeout} |
%%          {stop, Reason, State}            (terminate/2 is called)
%% --------------------------------------------------------------------
handle_cast(Msg, State) ->
    log4erl:debug("LORD: who caST me: ~p", [Msg]),
    {noreply, State}.

%% --------------------------------------------------------------------
%% Function: handle_info/2
%% Description: Handling all non call/cast messages
%% Returns: {noreply, State}          |
%%          {noreply, State, Timeout} |
%%          {stop, Reason, State}            (terminate/2 is called)
%% --------------------------------------------------------------------
handle_info({nodedown, Node, _Info}, State) ->
    log4erl:info("nodedown: ~p, ~p", [Node, _Info]),

    ets:match_delete(ring_nodes, #ring_node{name=Node, _='_'}),
    ets:match_delete(ring_accessores, #ring_accessor{node=Node, _='_'}),
    update_ringaccessores(),
    
    {noreply, State};
handle_info(Info, State) ->
    log4erl:debug("ringlord receive info: ~p", [Info]),
    {noreply, State}.

%% --------------------------------------------------------------------
%% Function: terminate/2
%% Description: Shutdown the server
%% Returns: any (ignored by gen_server)
%% --------------------------------------------------------------------
terminate(_Reason, _State) ->
    ok.

%% --------------------------------------------------------------------
%% Func: code_change/3
%% Purpose: Convert process state when code is changed
%% Returns: {ok, NewState}
%% --------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%% --------------------------------------------------------------------
%%% Internal functions
%% --------------------------------------------------------------------

